package upool

import (
	"context"
	"errors"
	"fmt"
	"os"
	"runtime"
	"sync"
)

var (
	ErrTaskPoolClosed error = errors.New("task_pool already closed")
)

type TaskHandler interface {
	Execute()
}

type TaskOption struct {
	RecoverHandler func(interface{})
	LogLevel       LogLevel
	IdleTime       int // seconds
}

type TaskPool struct {
	sync.Mutex

	capacity int // max     goroutine
	worker   int // active  goroutine
	running  int // running goroutine

	closed bool

	taskChan chan TaskHandler
	ctx      context.Context
	cancel   context.CancelFunc
}

var (
	defaultOption = TaskOption{
		LogLevel: WarnLevel,
		IdleTime: 1,
		RecoverHandler: func(recoverErr interface{}) {
			buf := make([]byte, 1024)
			buf = buf[:runtime.Stack(buf, false)]
			_, _ = os.Stderr.WriteString(fmt.Sprintf("panic: %v\n%s\n", recoverErr, buf))
		},
	}

	cfg TaskOption
)

func NewTaskPool(capacity int, options ...TaskOption) *TaskPool {
	if capacity <= 0 {
		return nil
	}

	if len(options) == 0 {
		cfg = defaultOption
	} else {
		cfg = options[0]

		if cfg.IdleTime == 0 {
			cfg.IdleTime = defaultOption.IdleTime
		}

		if cfg.LogLevel == 0 {
			cfg.LogLevel = WarnLevel
		}

		if cfg.RecoverHandler == nil {
			cfg.RecoverHandler = defaultOption.RecoverHandler
		}
	}

	setLogLevel(cfg.LogLevel)

	ctx, cancel := context.WithCancel(context.Background())

	log.Debugf("[UPool] new task pool with cap: %d", capacity)

	return &TaskPool{
		capacity: capacity,
		taskChan: make(chan TaskHandler),
		ctx:      ctx,
		cancel:   cancel,
	}
}
